package com.stark.commons.spring.core.boot.config;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.stark.commons.lang.util.Utils;
import com.stark.commons.spring.core.aspect.DataSourceRouter;
import com.stark.commons.spring.core.boot.properties.DataSourceProperties;
import com.stark.commons.spring.core.context.DataSourceContextHolder;
import com.stark.commons.spring.core.support.sql.DataSourceLookupKeyProvider;
import com.stark.commons.spring.core.support.sql.RoutingDataSource;

/**
 * 数据源自动配置。
 * @author Ben
 * @since 1.0.0
 * @version 1.0.0
 */
@Configuration
public class DataSourceAutoConfig {
	
	@Configuration
	@ConditionalOnClass(name = "org.springframework.jdbc.core.JdbcTemplate")
	@ConditionalOnProperty(prefix = "spring.datasource", name = "multiple", havingValue = "false", matchIfMissing = true)
	@EnableConfigurationProperties(DataSourceProperties.class)
	@EnableTransactionManagement
	@EnableCaching(proxyTargetClass = true)
	protected static class DefaultConfig extends DataSourceAutoConfiguration {
		
	}
	
	@Configuration
	@ConditionalOnClass(name = "org.springframework.jdbc.core.JdbcTemplate")
	@ConditionalOnProperty(prefix = "spring.datasource", name = "multiple", havingValue = "true")
	@EnableConfigurationProperties(DataSourceProperties.class)
	@EnableTransactionManagement
	@EnableCaching(proxyTargetClass = true)
	protected static class RoutingConfig {
		
		@Bean
		@Primary
		public DataSource routingDataSource(List<RoutingDataSource> dataSources) {
			RoutingDataSource defaultTargetDataSource = null;
			Map<Object, Object> targetDataSources = new HashMap<>();
			for (RoutingDataSource dataSource : dataSources) {
				targetDataSources.put(dataSource.getLookupKey(), dataSource);
				if (dataSource.isPrimary()) {
					defaultTargetDataSource = dataSource;
				}
			}
			if (defaultTargetDataSource == null) {
				throw new RuntimeException("未指定主数据源");
			}
			
			DataSourceContextHolder dataSourceContextHolder = new DataSourceContextHolder();
			dataSourceContextHolder.setDefaultTargetDataSource(defaultTargetDataSource);
			dataSourceContextHolder.setTargetDataSources(targetDataSources);
			DataSourceContextHolder.setDefaultKey(defaultTargetDataSource.getLookupKey());
			return dataSourceContextHolder;
		}
		
		@SuppressWarnings("unchecked")
		@Bean
		public DataSourceRouter dataSourceRouter(DataSourceProperties dataSourceProperties) {
			Class<? extends DataSourceLookupKeyProvider> lookupKeyProviderClass = null;
			String LOOKUP_KEY_PROVIDER = "spring.datasource.lookup-key-provider";
			if (StringUtils.isNotBlank(dataSourceProperties.getLookupKeyProvider())) {
				Class<?> _lookupKeyProviderClass = null;
				try {
					_lookupKeyProviderClass = Class.forName(dataSourceProperties.getLookupKeyProvider());
				} catch (Exception e) {
					throw new RuntimeException("找不到 " + LOOKUP_KEY_PROVIDER + " 指定的类 " + dataSourceProperties.getLookupKeyProvider(), e);
				}
				if (!Utils.isAssignableFrom(DataSourceLookupKeyProvider.class, dataSourceProperties.getLookupKeyProvider())) {
					throw new RuntimeException(LOOKUP_KEY_PROVIDER + " 指定的类 " + dataSourceProperties.getLookupKeyProvider() + " 必须是 " + DataSourceLookupKeyProvider.class.getName() + " 的子类");
				}
				lookupKeyProviderClass = (Class<? extends DataSourceLookupKeyProvider>) _lookupKeyProviderClass;
    		}
			
			DataSourceLookupKeyProvider lookupKeyProvider = null;
			if (lookupKeyProviderClass != null) {
				try {
					lookupKeyProvider = lookupKeyProviderClass.newInstance();
				} catch (Exception e) {
					throw new RuntimeException("实例化 " + LOOKUP_KEY_PROVIDER + " 指定的类 " + dataSourceProperties.getLookupKeyProvider() + " 失败", e);
				}
			}
			return new DataSourceRouter(lookupKeyProvider);
		}
		
	}
	
}
